package com.l2jserver.gameserver.datatables;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.items.L2Item;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.network.serverpackets.ExBrBuyProduct;
import com.l2jserver.gameserver.network.serverpackets.ExBrProductInfo;
import com.l2jserver.gameserver.network.serverpackets.ItemList;
import com.l2jserver.gameserver.util.Util;

/**
 * @author fissban
 */
public class PrimeShopTable extends DocumentParser
{
	private static final Logger _log = Logger.getLogger(PrimeShopTable.class.getName());
	
	private final Map<Integer, PrimeShopItem> _primeItems = new HashMap<>();
	
	public final int BR_BUY_SUCCESS = 1;
	public final int BR_BUY_LACK_OF_POINT = -1;
	public final int BR_BUY_INVALID_PRODUCT = -2;
	public final int BR_BUY_USER_CANCEL = -3;
	public final int BR_BUY_INVENTROY_OVERFLOW = -4;
	public final int BR_BUY_CLOSED_PRODUCT = -5;
	public final int BR_BUY_SERVER_ERROR = -6;
	public final int BR_BUY_BEFORE_SALE_DATE = -7;
	public final int BR_BUY_AFTER_SALE_DATE = -8;
	public final int BR_BUY_INVALID_USER = -9;
	public final int BR_BUY_INVALID_ITEM = -10;
	public final int BR_BUY_INVALID_USER_STATE = -11;
	public final int BR_BUY_NOT_DAY_OF_WEEK = -12;
	public final int BR_BUY_NOT_TIME_OF_DAY = -13;
	public final int BR_BUY_SOLD_OUT = -14;
	
	public PrimeShopTable()
	{
		if (Config.PRIME_SHOP_ENABLE)
		{
			load();
		}
		else
		{
			_log.info("[PrimeShop] Disabled");
		}
	}
	
	@Override
	public void load()
	{
		_primeItems.clear();
		parseDatapackFile("data/PrimeShop.xml");
	}
	
	public final void reload()
	{
		_primeItems.clear();
		load();
	}
	
	@Override
	protected void parseDocument()
	{
		for (Node a = getCurrentDocument().getFirstChild(); a != null; a = a.getNextSibling())
		{
			if (!"list".equalsIgnoreCase(a.getNodeName()))
			{
				continue;
			}
			for (Node b = a.getFirstChild(); b != null; b = b.getNextSibling())
			{
				if (!"item".equalsIgnoreCase(b.getNodeName()))
				{
					continue;
				}
				NamedNodeMap attrs = b.getAttributes();
				
				int type = 0;
				
				Node att = attrs.getNamedItem("brId");
				if (att == null)
				{
					_log.severe("[PrimeShop] Missing brId, skipping");
					continue;
				}
				final int brId = Integer.parseInt(att.getNodeValue());
				
				att = attrs.getNamedItem("itemId");
				if (att == null)
				{
					_log.severe("[PrimeShop] Missing itemId, skipping");
					continue;
				}
				final int itemId = Integer.parseInt(att.getNodeValue());
				
				att = attrs.getNamedItem("cat");
				if (att == null)
				{
					_log.severe("[PrimeShop] Missing category, skipping");
					continue;
				}
				final int cat = Integer.parseInt(att.getNodeValue());
				
				att = attrs.getNamedItem("price");
				if (att == null)
				{
					_log.severe("[PrimeShop] Missing price, skipping");
					continue;
				}
				final int price = Integer.parseInt(att.getNodeValue());
				
				att = attrs.getNamedItem("count");
				if (att == null)
				{
					_log.severe("[PrimeShop] Missing count, skipping");
					continue;
				}
				final int count = Integer.parseInt(att.getNodeValue());
				
				att = attrs.getNamedItem("event");
				if (att == null)
				{
					_log.severe("[PrimeShop] Missing event, skipping");
					continue;
				}
				final boolean event = Boolean.parseBoolean(att.getNodeValue());
				
				att = attrs.getNamedItem("best");
				if (att == null)
				{
					_log.severe("[PrimeShop] Missing best, skipping");
					continue;
				}
				final boolean best = Boolean.parseBoolean(att.getNodeValue());
				
				if (event)
				{
					type++;
				}
				if (best)
				{
					type += 2;
				}
				L2Item item = ItemTable.getInstance().getTemplate(itemId);
				if (item == null)
				{
					_log.severe("[PrimeShop] Item template null");
				}
				else
				{
					att = attrs.getNamedItem("sale_start_date");
					if (att == null)
					{
						_log.severe("[PrimeShop] Missing brId, skipping");
						continue;
					}
					final String sale_start_date = att.getNodeValue();
					
					att = attrs.getNamedItem("sale_end_date");
					if (att == null)
					{
						_log.severe("[PrimeShop] Missing brId, skipping");
						continue;
					}
					final String sale_end_date = att.getNodeValue();
					
					_primeItems.put(Integer.valueOf(brId), new PrimeShopItem(itemId, cat, price, count, item.getWeight(), item.isTradeable(), type, sale_start_date, sale_end_date));
				}
			}
		}
		
		if (_primeItems.size() > 0)
		{
			_log.info("[PrimeShop] Loaded: " + _primeItems.size() + " items");
		}
	}
	
	public int getMaxStock(int brId)
	{
		int maxStock = 0;
		
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("SELECT maxStock FROM primeshop WHERE brId=" + brId))
		{
			try (ResultSet rset = statement.executeQuery())
			{
				while (rset.next())
				{
					return maxStock = rset.getInt("maxStock");
				}
			}
		}
		catch (Exception e)
		{
			_log.log(Level.WARNING, "Exception: error while loading get stock for product: " + brId + e, e);
		}
		return maxStock;
	}
	
	public int getActualStock(int brId)
	{
		int getActualStock = 0;
		
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("SELECT getStock FROM primeshop WHERE brId=?"))
		{
			statement.setInt(1, brId);
			try (ResultSet rset = statement.executeQuery())
			{
				while (rset.next())
				{
					return getActualStock = rset.getInt("getStock");
				}
			}
		}
		catch (Exception e)
		{
			_log.log(Level.WARNING, "Exception: error while loading actual stock for product: " + brId + e, e);
		}
		return getActualStock;
	}
	
	public void discountStock(int brId, int Stock, int count)
	{
		Stock += count;
		
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement statement = con.prepareStatement("UPDATE primeshop SET getStock=? WHERE brId=?"))
		{
			statement.setInt(1, Stock);
			statement.setInt(2, brId);
			statement.execute();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public void buyItem(L2PcInstance player, int brId, int count)
	{
		if ((count < 1) && (count > 99))
		{
			Util.handleIllegalPlayerAction(player, "Player " + player.getName() + " tried to buy invalid itemcount [" + count + "] from Prime", Config.DEFAULT_PUNISH);
			return;
		}
		
		int Stock = getActualStock(brId);
		int MaxStock = getMaxStock(brId);
		
		if (MaxStock != 0) // TODO solo sale 0 cuando no tienen maximo de stock
		{
			if ((MaxStock - Stock) < count)
			{
				player.sendPacket(new ExBrBuyProduct(-14)); // TODO no puede comprar tantos items
				return;
			}
		}
		
		if (_primeItems.containsKey(Integer.valueOf(brId)))
		{
			PrimeShopItem item = _primeItems.get(Integer.valueOf(brId));
			
			long points = 0;
			
			if (Config.GAME_POINT_ITEM_ID == -1)
			{
				points = player.getgame_points();
			}
			else
			{
				points = player.getInventory().getInventoryItemCount(Config.GAME_POINT_ITEM_ID, -1);
			}
			
			// TODO hace falta poner las 2 condiciones? quizas solo con la segunda y sin el && ultimo deberia funcionar
			if (points >= (item.getPrimeItemPrice() * count))
			{
				L2ItemInstance dummy = ItemTable.getInstance().createDummyItem(item.getPrimeItemId());
				if (dummy != null)
				{
					int weight = item.getPrimeItemCount() * item.getPrimeWeight() * count;
					
					if (player.getInventory().validateWeight(weight))
					{
						int slots = 0;
						if (dummy.isStackable())
						{
							slots = 1;
						}
						else
						{
							slots = item.getPrimeItemCount() * count;
						}
						if (player.getInventory().validateCapacity(slots))
						{
							if (player.addItem("PrimeShop", item.getPrimeItemId(), count * item.getPrimeItemCount(), player, true) != null)
							{
								player.sendPacket(new ExBrBuyProduct(1));
								
								if (Config.GAME_POINT_ITEM_ID != -1)
								{
									player.getInventory().destroyItemByItemId("PrimeShopDes", Config.GAME_POINT_ITEM_ID, item.getPrimeItemPrice() * count, player, false);
									player.sendPacket(new ItemList(player, false));// TODO necesario?
								}
								else
								{
									player.setgame_points(player.getgame_points() - (item.getPrimeItemPrice() * count));
								}
								
								if (MaxStock != 0)
								{
									discountStock(brId, Stock, count);
								}
							}
							else
							{
								player.sendPacket(new ExBrBuyProduct(-6));
							}
						}
						else
						{
							player.sendPacket(new ExBrBuyProduct(-4));
						}
					}
					else
					{
						player.sendPacket(new ExBrBuyProduct(-4));
					}
				}
				else
				{
					player.sendPacket(new ExBrBuyProduct(-6));
				}
			}
			else
			{
				player.sendPacket(new ExBrBuyProduct(-1));
			}
		}
		else
		{
			player.sendPacket(new ExBrBuyProduct(-2));
			Util.handleIllegalPlayerAction(player, "Player " + player.getName() + " tried to buy invalid brId from Prime", Config.DEFAULT_PUNISH);
		}
	}
	
	public void showProductInfo(L2PcInstance player, int itemId)
	{
		if ((player == null) || (itemId == 0))
		{
			return;
		}
		if (_primeItems.containsKey(itemId))
		{
			PrimeShopItem item = _primeItems.get(itemId);
			
			player.sendPacket(new ExBrProductInfo(itemId, item));
		}
	}
	
	public Map<Integer, PrimeShopItem> getPrimeItems()
	{
		return _primeItems;
	}
	
	public class PrimeShopItem
	{
		int _itemId;
		int _category;
		int _price;
		int _count;
		int _weight;
		int _tradable;
		int _type;
		
		long _sale_start_date;
		long _sale_end_date;
		
		int _startHour;
		int _endHour;
		int _startMin;
		int _endMin;
		
		public PrimeShopItem(int itemId, int category, int price, int count, int weight, boolean tradable, int type, String sale_start_date, String sale_end_date)
		{
			new Date().getTime();
			
			_itemId = itemId;
			_category = category;
			_price = price;
			_weight = weight;
			_count = count;
			_tradable = (tradable ? 1 : 0);
			_type = type;
			
			DateFormat df = new SimpleDateFormat("yyyy.MM.dd HH:mm");
			try
			{
				Date time_start = df.parse(sale_start_date);
				Date time_end = df.parse(sale_end_date);
				Calendar calendar = Calendar.getInstance();
				
				calendar.setTime(time_start);
				_sale_start_date = time_start.getTime();
				_startHour = calendar.get(Calendar.HOUR_OF_DAY);
				_startMin = calendar.get(Calendar.MINUTE);
				
				calendar.setTime(time_end);
				_sale_end_date = time_end.getTime();
				_endHour = calendar.get(Calendar.HOUR_OF_DAY);
				_endMin = calendar.get(Calendar.MINUTE);
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
		
		public int getPrimeItemId()
		{
			return _itemId;
		}
		
		public int getPrimeItemCat()
		{
			return _category;
		}
		
		public int getPrimeItemPrice()
		{
			return _price;
		}
		
		public int getPrimeItemCount()
		{
			return _count;
		}
		
		public int getPrimeWeight()
		{
			return _weight;
		}
		
		public int getPrimeTradable()
		{
			return _tradable;
		}
		
		public long sale_start_date()
		{
			return _sale_start_date;
		}
		
		public long sale_end_date()
		{
			return _sale_end_date;
		}
		
		public int getStartHour()
		{
			return _startHour;
		}
		
		public int getStartMin()
		{
			return _startMin;
		}
		
		public int getEndHour()
		{
			return _endHour;
		}
		
		public int getEndMin()
		{
			return _endMin;
		}
		
		public int getPrimeType()
		{
			return _type;
		}
	}
	
	public PrimeShopItem getProduct(int id)
	{
		return _primeItems.get(id);
	}
	
	public static PrimeShopTable getInstance()
	{
		return SingletonHolder._instance;
	}
	
	private static class SingletonHolder
	{
		protected static final PrimeShopTable _instance = new PrimeShopTable();
	}
}